<template>
  <div class="lf-graph-main">
    <!-- 画板容器 -->
    <div ref="lfElRef" class="container"></div>
    <!-- 属性面板 -->
    <PropertSetting
      ref="propertSettingRef"
      :extendAttrConfig="props.extendAttrConfig"
      @change="handlePropertyChange"
    >
      <!-- 动态渲染外部传入的内容 -->
      <template #[key]="data" v-for="(item, key) in $slots">
        <slot :name="key" v-bind="data || {}"></slot>
      </template>
    </PropertSetting>
    <!-- 查看明细 -->
    <DataDetail ref="dataDetailRef" />
    <!-- 导入数据 -->
    <ImportData ref="importDataRef" @on-submit="handleImportSubmit" />
    <!-- 设置高亮 -->
    <HighLightData ref="highLightDataRef" @on-submit="handleHighLightSubmit" />
  </div>
</template>

<script lang="ts">
import './style.css'
//@ts-ignore
import LogicFlow from '@logicflow/core'
//@ts-ignore
import {Snapshot, DndPanel, SelectionSelect, Menu, Control, Group, MiniMap} from '@logicflow/extension'
import '@logicflow/core/dist/style/index.css'
import '@logicflow/extension/lib/style/index.css'
import {onMounted, type Ref, ref, unref, defineProps, watch, defineExpose, defineEmits, provide,computed} from 'vue'
import {type ProcessModel, type PropertyEvent} from './types'
//@ts-ignore
import PropertSetting from './tools/index.vue'
import {WorkingFlowerElement,WorkingFlowerAdapter} from './nodes/index'
import {NodeTypeEnum} from './enums'

//@ts-ignore
import DataDetail from './nodes/control/DataDetail.vue'
//@ts-ignore
import ImportData from './nodes/control/ImportData.vue'
//@ts-ignore
import HighLightData from './nodes/control/HighLightData.vue'

// 定义组件名称
export default {
    name: 'ingeniousDesigner',
}
</script>

<script lang="ts" setup>
const emits = defineEmits(["update:modelValue", "on-save"]);

// 定义挂载元素Ref
const lfElRef: Ref = ref(null);
// 属性面板操作
const propertSettingRef: Ref = ref(null);
// 查看数据弹窗
const dataDetailRef: Ref = ref(null);
// 导入数据弹窗
const importDataRef: Ref = ref(null);

// 导入高高弹窗
const highLightDataRef: Ref = ref(null);
// 定义LogicFlow实例
const lfInstance = ref(null) as Ref<LogicFlow | null>;
// 当前操作节点/或边id
const currentOpId = ref("");
// 定义组件接收的参数
const props = defineProps({
  modelValue: {
    type: [Object, String],
  },
  config: {
    type: Object,
    default() {
      return {
        grid: {
          size: 20,
          visible: true,
          type: "dot", //目前只支持dot mesh
          config: {
            color: "#ababab",
            thickness: 1,
          },
        },
      };
    },
  },
  highLight: {
    // 高亮数据
    type: Object,
    default() {
      return {};
    },
  },
  viewer: {
    // 预览模式
    type: Boolean,
    default: false,
  },
  extendAttrConfig: {
    // 扩展属性配置
    type: Object,
  },
});

// 监听流程变化
watch(
  () => props.modelValue,
  () => {
    reRender(props.modelValue);
  },
  {
    deep: true,
  }
);
// 监听高亮数据
watch(
  () => props.highLight,
  () => {
    setHighLight(props.highLight);
  },
  {
    deep: true,
  }
);

// 初始化
const init = () => {
  // 画布配置
  LogicFlow.use(Snapshot);
  LogicFlow.use(DndPanel);
  LogicFlow.use(SelectionSelect);
  LogicFlow.use(Menu);
  LogicFlow.use(Control);
  LogicFlow.use(WorkingFlowerElement);
  LogicFlow.use(WorkingFlowerAdapter);
  LogicFlow.use(Group);
  LogicFlow.use(MiniMap);
  const defaultConfig: any = {};
  if (props.viewer) {
    defaultConfig.isSilentMode = true;
  }

  lfInstance.value = new LogicFlow({
    container: unref(lfElRef),
    grid: true,
    ...props.config,
    ...defaultConfig,
    keyboard: {
      enabled: true,
      shortcuts: [
        {
          keys: ["del"],
          callback: () => {
            const lf = unref(lfInstance) as LogicFlow;
            if (!lf) return;
            const elements = lf.getSelectElements(true);
            lf.clearSelectElements();
            elements.edges.forEach((edge) => lf.deleteEdge(edge.id as string));
            elements.nodes.forEach((node) => lf.deleteNode(node.id as string));
          },
        },
      ],
    },
  });
  // 初始化操作
  initOp();
  // 初始化事件
  initEvent();
  reRender(props.modelValue as ProcessModel);
};
// 初始化操作
const initOp = () => {
  const lf = unref(lfInstance);
  if (!lf) return;
  if (props.viewer) {
    // 预览模式时
    lf.extension.menu.setMenuConfig({
      nodeMenu: [],
      edgeMenu: [],
    });
    // 删除上一步
    lf.extension.control.removeItem("undo");
    // 删除下一步
    lf.extension.control.removeItem("redo");
    return;
  }
  // 控制面板-清空画布
  lf.extension.control.addItem({
    iconClass: "lf-control-clear",
    title: "clear",
    text: "清空",
    onClick: () => {
      lf.clearData();
    },
  });
  // 控制面板-添加查看按钮
  lf.extension.control.addItem({
    iconClass: "lf-control-see",
    title: "",
    text: "查看",
    onClick: () => {
      const dataDetailElRef = unref(dataDetailRef);
      if (!dataDetailElRef) return;
      const graphData = getGraphData();
      dataDetailElRef.show(graphData);
    },
  });
  // 控制面板-添加导入按钮
  lf.extension.control.addItem({
    iconClass: "lf-control-import",
    title: "",
    text: "导入",
    onClick: () => {
      const importDataElRef = unref(importDataRef);
      if (!importDataElRef) return;
      importDataElRef.show();
    },
  });
  // 控制面板-设置高亮数据
  lf.extension.control.addItem({
    iconClass: "lf-control-setting",
    title: "",
    text: "设置高亮",
    onClick: () => {
      const highLightDataElRef = unref(highLightDataRef);
      if (!highLightDataElRef) return;
      highLightDataElRef.show();
    },
  });
  // 控制面板-保存
  lf.extension.control.addItem({
    iconClass: "lf-control-save",
    title: "",
    text: "保存",
    onClick: () => {
      emits("on-save", getGraphData());
    },
  });
  //小地图
  lf.extension.control.addItem({
    iconClass: "custom-minimap",
    title: "",
    text: "导航",
    onClick: (lf: LogicFlow, ev: any) => {
      const position = lf.getPointByClient(ev.x, ev.y);
      lf.extension.miniMap.show(
        position.domOverlayPosition.x - 120,
        position.domOverlayPosition.y + 40
      );
    },
  });
  // 设置默认边
  lf.setDefaultEdgeType("ingenious:transition");
  // 设置拖拽面板
  lf.extension.dndPanel.setPatternItems([
    {
      type: "ingenious:start",
      text: "开始",
      label: "开始节点",
      properties: {},
      icon:
        "",
    },
    {
      type: "ingenious:task",
      text: "用户",
      label: "用户任务",
      properties: {},
      icon:
        "",
      className: "important-node",
    },
    {
      type: "ingenious:custom",
      text: "系统",
      label: "系统任务",
      properties: {},
      icon:
        "",
      className: "import_icon",
    },
    {
      type: "ingenious:decision",
      label: "条件判断",
      properties: {},
      icon:
        "",
    },
    {
      type: "ingenious:fork",
      label: "分支",
      properties: {},
      icon:
        "",
    },
    {
      type: "ingenious:join",
      label: "合并",
      properties: {},
      icon:
        "",
    },
    {
      type: "ingenious:end",
      text: "结束节点",
      label: "结束节点",
      properties: {},
      icon:
        "",
    },
        {
      type: 'ingenious:wfSubProcess',
      text: '子流程',
      label: '子流程',
      properties: {},
      icon: '',
      className: 'import_icon'
    },
    // {
    //   type: "ingenious:subProcess",
    //   text: "",
    //   label: "子流程",
    //   properties: {},
    //   icon:
    //     "",
    //   className: "import_icon",
    // },
  ]);
};

// 初始化事件
const initEvent = () => {
  if (props.viewer) return;
  // 初始化事件
  const lf = unref(lfInstance);
  if (!lf) return;
  // 空白区右键事件-弹出流程属性表单
  const eventCenter = lf.graphModel.eventCenter;
  eventCenter.on("blank:contextmenu", (args) => {
    propertSettingRef.value.show({
      name: lf.graphModel.name,
      displayName: lf.graphModel.displayName,
      expireTime: lf.graphModel.expireTime,
      instanceUrl: lf.graphModel.instanceUrl,
      instanceNoClass: lf.graphModel.instanceNoClass,
      type: "ingenious:process",
    });
  });
  // 节点点击事件
  eventCenter.on("node:click", (args) => {
    currentOpId.value = args.data.id;
    propertSettingRef.value.show({
      ...args.data.properties,
      name: args.data.id,
      displayName: args.data.text?.value,
      type: args.data.type,
    });
  });
  // 边点击事件
  eventCenter.on("edge:click", (args) => {
    currentOpId.value = args.data.id;
    propertSettingRef.value.show({
      ...args.data.properties,
      name: args.data.id,
      displayName: args.data.text?.value,
      type: args.data.type,
    });
  });
  // 空白处点击
  eventCenter.on("blank:click", (args) => {
    if (propertSettingRef.value) {
      propertSettingRef.value.handleClose();
    }
  });
};
// 重新渲染
const reRender = (data: any): void => {
  const lf = unref(lfInstance);
  if (!lf) return;
  lf.render(data);
  // 设置高亮
  setHighLight(props.highLight);
};

// 处理属性值变化事件
const handlePropertyChange = (e: PropertyEvent) => {
  const lf = unref(lfInstance);
  if (!lf) return;
  if (e.type === NodeTypeEnum.process) {
    // 流程属性
    lf.graphModel[e.propertyName] = e.propertyValue;
  } else if (e.type === NodeTypeEnum.subProcess) {
    // 节点属性
    const nodeId = unref(currentOpId);
    if (e.propertyName === "name") {
      // 更新唯一标识
      if (!lf.getNodeModelById(e.propertyValue)) {
        lf.changeNodeId(nodeId, e.propertyValue);
        currentOpId.value = e.propertyValue;
      }
    } else {
      lf.setProperties(nodeId, {
        [e.propertyName]: e.propertyValue,
      });
    }
  } else if (
    ([
      NodeTypeEnum.custom,
      NodeTypeEnum.decision,
      NodeTypeEnum.end,
      NodeTypeEnum.fork,
      NodeTypeEnum.join,
      NodeTypeEnum.start,
      NodeTypeEnum.task,
    ] as NodeTypeEnum[]).includes(e.type)
  ) {
    // 节点属性
    const nodeId = unref(currentOpId);
    // 节点信息
    if (e.propertyName === "name") {
      // 更新唯一标识
      if (!lf.getNodeModelById(e.propertyValue)) {
        lf.changeNodeId(nodeId, e.propertyValue);
        currentOpId.value = e.propertyValue;
      }
    } else if (e.propertyName === "display_name") {
      // 更新节点文本值
      lf.updateText(nodeId, e.propertyValue);
    } else if (
      e.propertyName === "width" &&
      [NodeTypeEnum.task, NodeTypeEnum.custom].includes(e.type)
    ) {
      // 宽度
      lf.setProperties(nodeId, {
        width: (Number.isNaN(e.propertyValue) ? 120 : e.propertyValue) as number,
      });
      const nodeModel = lf.getNodeModelById(nodeId);
      if (nodeModel) {
        nodeModel.width = (Number.isNaN(e.propertyValue)
          ? 120
          : e.propertyValue) as number;
      }
    } else if (
      e.propertyName === "height" &&
      [NodeTypeEnum.task, NodeTypeEnum.custom].includes(e.type)
    ) {
      // 高度
      lf.setProperties(nodeId, {
        height: (Number.isNaN(e.propertyValue) ? 40 : e.propertyValue) as number,
      });
      const nodeModel = lf.getNodeModelById(nodeId);
      if (nodeModel) {
        nodeModel.height = (Number.isNaN(e.propertyValue)
          ? 40
          : e.propertyValue) as number;
      }
    } else if (e.type === "field") {
      // 更新扩展属性
      lf.setProperties(nodeId, {
        field: JSON.stringify(e.propertyValue),
      });
    } else {
      // 更新基础属性
      lf.setProperties(nodeId, {
        [e.propertyName]: e.propertyValue,
      });
    }

    emits("update:modelValue", getGraphData());
  } else if (e.type === NodeTypeEnum.transition) {
    // 边属性
    const edgeId = unref(currentOpId);
    if (e.propertyName === "name") {
      // 更新唯一标识
      if (!lf.getEdgeModelById(e.propertyValue)) {
        lf.changeEdgeId(edgeId, e.propertyValue);
        currentOpId.value = e.propertyValue;
      }
    } else if (e.propertyName === "display_name") {
      // 更新节点文本值
      lf.updateText(edgeId, e.propertyValue);
    } else {
      // 更新基础属性
      lf.setProperties(edgeId, {
        [e.propertyName]: e.propertyValue,
      });
    }
  }
};
/**
 * 获取流程数据
 */
const getGraphData = () => {
  const lf = unref(lfInstance);
  if (!lf) return {};
  return lf.getGraphData();
};

/**
 * 处理导入提交事件
 */
const handleImportSubmit = (str: string) => {
  let data: any = null;
  try {
    data = JSON.parse(str);
  } catch {
    return;
  }
  if (data && data.nodes && data.edges) {
    reRender(data);
  }
};
// 导入json
const importJson = (data: any) => {
  reRender(data);
};

// 刷新导入下拉数据
const refreshImport = () => {
  return unref(importDataRef)?.refresh();
};
// 导入高亮数据
const handleHighLightSubmit = (jsonStr: string) => {
  let data: any = null;
  try {
    data = JSON.parse(jsonStr);
  } catch {}
  setHighLight(data);
};
/**
 * 设置高亮数据
 * @param data { "historyNodeNames": [], "historyEdgeNames": [], "activeNodeNames": []}
 */
const setHighLight = (data: any) => {
  const lf = unref(lfInstance);
  if (!lf) return;
  // 设置历史节点
  if (data && data.historyNodeNames) {
    data.historyNodeNames.forEach((nodeName: string) => {
      lf.setProperties(nodeName, { state: "history" });
    });
  }
  // 设置当前节点
  if (data && data.activeNodeNames) {
    data.activeNodeNames.forEach((nodeName: string) => {
      lf.setProperties(nodeName, { state: "active" });
    });
  }
  // 设置历史边
  if (data && data.historyEdgeNames) {
    data.historyEdgeNames.forEach((edgeName: string) => {
      const edgeModel = lf.getEdgeModelById(edgeName);
      if (edgeModel) {
        edgeModel.setProperties({ state: "history" });
      }
    });
  }
};
onMounted(init);
// 导出提供给外部操作-$refs.xxx
defineExpose({
  getGraphData,
  refreshImport,
  importJson,
  setHighLight,
});
</script>

<style scoped>
.container {
  width: 100%;
  height: 100%;
}

.lf-graph-main {
  position: relative;
  width: 100%;
  height: 100%;
}

.lf-graph-main /deep/ .lf-red-node,
.lf-graph-main /deep/ .lf-element-text {
  cursor: move;
}

.lf-graph-main /deep/ svg {
  display: block;
}

.lf-graph-main-palette {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 1;
}

.setting-panel {
  position: absolute;
  top: 0;
  right: 0;
}
</style>
<!-- 按钮全局样式 -->
<style>
.lf-control-see {
  background-image: url("");
}

.lf-control-import {
  background-image: url("");
}

.lf-control-clear {
  background-image: url("");
}

.lf-control-save {
  background-image: url("");
}

.custom-minimap {
  background-image: url();
}

.lf-control-join {
  width: 70px;
  height: 55px;
  background-repeat: no-repeat;
  background-size: 35px 30px;
  background-position: center;
  background-image: url("");
}

.lf-control-fork {
  width: 70px;
  height: 55px;
  background-repeat: no-repeat;
  background-size: 35px 30px;
  background-position: center;
  background-image: url("");
}

.lf-control-setting {
  width: 70px;
  height: 55px;
  background-repeat: no-repeat;
  background-size: 35px 30px;
  background-position: center;
  background-image: url("");
}

.lf-control-text,
.lf-dnd-text,
.lf-menu-item {
  color: black;
}

.lf-menu {
  background: #dce2e8;
}

.lf-node {
  user-select: none !important;
}
</style>
